package idx

import (
	"crypto/md5"
	"fmt"
	"hash"
	"sync/atomic"
	"time"
)

// NewDatWriter 创建一个写入流对象
func (f *FileDat) NewDatWriter(key string, size int64, expire time.Time) *FileDatWriter {
	f.mux.Lock()
	defer f.mux.Unlock()
	//设置idx
	idx := NewIdx(key, size)
	datOffset := f.size + idx.Count()
	idx.SetExpire(expire)
	idx.SetOffset(datOffset)
	f.size = datOffset + size
	writeIdxOnce := uint32(0)
	f.writeRefIncr(1)
	//f.add()
	return &FileDatWriter{
		fd:           f,
		idx:          idx,
		writeIdxOnce: &writeIdxOnce,

		offset: datOffset,
		size:   size,
		md5:    md5.New(),
	}
}

// FileDatWriter fileDat 写入对象
type FileDatWriter struct {
	fd           *FileDat
	idx          *Idx
	writeIdxOnce *uint32

	offset int64     //dat写入位置
	size   int64     //dat总写入
	n      int64     //已经写入
	md5    hash.Hash //md5
}

func (f *FileDatWriter) Write(b []byte) (n int, err error) {
	if f.size == f.n {
		return 0, ErrFileDat.New("write finish")
	}
	l := len(b)
	if l > int(f.size-f.n) {
		buf := make([]byte, f.size-f.n)
		copy(buf, b)
		f.md5.Write(buf)
		n, err = f.fd.fp.WriteAt(buf, f.offset+f.n)
	} else {
		f.md5.Write(b)
		n, err = f.fd.fp.WriteAt(b, f.offset+f.n)
	}
	f.n += int64(n)

	//数据写入完成后的操作，要写入idx
	if f.n == f.size {
		_err := f.WriteIdx()
		if err == nil {
			err = _err
		}
	}
	err = ErrFileDat.Wrap(err)
	return
}

func (f *FileDatWriter) Offset() int64 {
	return f.offset
}

func (f *FileDatWriter) Size() int64 {
	return f.size
}

func (f *FileDatWriter) Md5() []byte {
	return f.idx.Md5
}

func (f *FileDatWriter) Close() error {
	//f.file.done()
	f.fd.writeRefIncr(-1)
	return nil
}

// WriteIdx 只写入一次
func (f *FileDatWriter) WriteIdx() error {
	if atomic.CompareAndSwapUint32(f.writeIdxOnce, 0, 1) {
		f.idx.SetMd5(f.md5.Sum(nil))
		_, _e := f.fd.addIdx(f.idx)
		if _e != nil {
			return fmt.Errorf("idx[%s]写入错误: %s", f.idx.Name, _e)
		}
	}
	return nil
}
